package org.hw.juc;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/*

一、创建异步任务
    CompletableFuture创建异步任务，常用的方法有两个。
    1、runAsync()：执行异步任务时，没有返回值
    2、supplyAsync()：执行异步任务时，可以带返回值

二、线程池：CompletableFuture也使用了线程池来执行任务
    1、当前机器 cpu 可用逻辑核心数大于 1，默认会采用ForkJoinPool.commonPool()线程池来执行任务
    2、当前机器 cpu 可用逻辑核心数等于 1，默认会采用ThreadPerTaskExecutor类来执行任务，它是个一对一执行器，每提交一个任务会创建一个新的线程来执行
    3、同时也支持用户传入自定义线程池来异步执行任务

三、任务回调：当创建的异步任务执行完毕之后，我们希望拿着上一个任务的执行结果，继续执行后续的任务，此时就可以采用回调方法来处理。
    CompletableFuture针对任务异步回调做了很多的支持，常用的方法如下：
    1、thenRun()/thenRunAsync()：它表示上一个任务执行成功后的回调方法，无入参，无返回值
    2、thenAccept()/thenAcceptAsync()：它表示上一个任务执行成功后的回调方法，有入参，无返回值
    3、thenApply()/thenApplyAsync()：它表示上一个任务执行成功后的回调方法，有入参，有返回值
    4、whenComplete()/whenCompleteAsync()：它表示任务执行完成后的回调方法，有入参，无返回值
    5、handle()/handleAsync()：它表示任务执行完成后的回调方法，有入参，有返回值
    6、exceptionally()：它表示任务执行异常后的回调方法
thenRun()、thenRunAsync()、thenAccept()、thenAcceptAsync()、thenApply()、thenApplyAsync()方法，当上一个任务执行异常，不会回调这些方法。
whenComplete()、whenCompleteAsync() 方法，无论任务执行成功还是失败，它都会回调
handle()、handleAsync() 方法，无论任务执行成功还是失败，它都会回调

四、任务组合：CompletableFuture针对多个任务组合处理做了很多的支持，常用的组合方式有以下几种。
    1、AND组合：表示将两个CompletableFuture任务组合起来，只有这两个任务都正常执行完了，才会继续执行回调任务，比如thenCombine()方法
        实现AND组合的操作方法有很多，比如runAfterBoth()、thenAcceptBoth()、thenCombine()等方法，它们之间的区别在于：是否带有入参、是否带有返回值。
        其中thenCombine()方法支持传入参、带返回值。
    2、OR组合：表示将两个CompletableFuture任务组合起来，只要其中一个正常执行完了，就会继续执行回调任务，比如applyToEither方法
        实现OR组合的操作方法有很多，比如runAfterEither()、acceptEither()、applyToEither()等方法，区别同上。
        其中applyToEither()方法支持传入参、带返回值
    3、AllOf组合：可以将多个CompletableFuture任务组合起来，只有所有的任务都正常执行完了，才会继续执行回调任务，比如allOf()方法
        实现AllOf组合的操作就一个方法allOf()，可以将多个任务进行组合，只有都执行成功才会回调，回调入参为空值。
    4、AnyOf组合：可以将多个CompletableFuture任务组合起来，只要其中一个任务正常执行完了，就会继续执行回调任务，比如anyOf()方法
        实现AnyOf组合的操作，同样就一个方法anyOf()，可以将多个任务进行组合，只要一个执行成功就会回调，回调入参有值。

*/

public class CompletableFutureDemo {
    static Supplier<MyTask> taskFactory = MyTask::new;

    public static void main(String[] args) throws Exception {
        MyTask task1 = taskFactory.get();
        task1.setName("task1");
        task1.setMsg("hello,this is task1");
        task1.setSecond(5000);
        MyTask task2 = taskFactory.get();
        task2.setName("task2");
        task2.setMsg("hello,this is task2");
        task2.setSecond(200);

        //自定义线程池
        ExecutorService executor = Executors.newFixedThreadPool(10);

        /*******************示例1**********************/
        CompletableFuture f = CompletableFuture.runAsync(() -> System.out.println("hello!"), executor);
        CompletableFuture<String> fs = CompletableFuture.supplyAsync(() -> "hello!", executor);

        //异步执行，无返回值
        CompletableFuture f1 = CompletableFuture.runAsync(() -> {
            System.out.println("hello!");
        });
        //异步执行，有返回值
        CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
            return "hello";
        });
        System.out.println(f2.get());

        /*******************示例2**********************/
        // 创建异步执行任务
        CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> task1.taskInfo());
        // 如果执行成功，回调此方法
        cf.thenAccept((result) -> {
            System.out.println("任务执行成功，返回结果值：" + result);
        });
        // 如果执行异常，回调此方法
        cf.exceptionally((e) -> {
            System.out.println("任务执行失败，原因：" + e.getMessage());
            return null;
        });
        // 当任务执行完成，不管是否有异常，都会继续回调当前方法
        CompletableFuture<String> cf1 = cf.whenComplete((r, e) -> {
            System.out.println("whenComplete，执行完毕，上一个任务执行结果值：" + r + "，异常信息：" + e.getMessage());
        });
        // 当上一个任务执行成功，会继续回调当前方法
        CompletableFuture<String> cf2 = cf.thenApply((r) -> {
            System.out.println("thenApply，执行完毕，上一个任务执行结果值：" + r);
            return "thenApply 完成！";
        });
        // 当上一个任务执行成功，会继续回调当前方法
        CompletableFuture<Void> cf3 = cf1.thenAccept((r) -> {
            System.out.println("thenAccept，执行完毕，上一个任务执行结果值：" + r);
        });
        // 当上一个任务执行成功，会继续回调当前方法
        CompletableFuture<Void> cf4 = cf.thenRun(() -> {
            System.out.println("thenRun1，执行完毕");
        });

        /*******************示例3**********************/
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> task1.taskInfo());
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> task2.taskInfo());
        future1.whenComplete((result, exception) -> {
            System.out.println("task1执行结束--------------");
            System.out.println("返回值是：" + result);
            System.out.println("错误是：" + exception);
        });
        future2.whenComplete((result, exception) -> {
            System.out.println("task2执行结束--------------");
            System.out.println("返回值是：" + result);
            System.out.println("错误是：" + exception);
        });
        System.out.println("task1返回值：" + future1.get());
        System.out.println("task2返回值：" + future2.get());

        /*******************示例4**********************/
        /*
         * thenApply 方法用于对 CompletableFuture 的结果进行转换。它接受一个 Function 接口作为参数，该函数将在原始 CompletableFuture 的结果上被调用，并返回一个新的 CompletableFuture。
         * thenCompose 方法用于将一个 CompletableFuture 的结果传递给另一个异步操作。它接受一个返回 CompletableFuture 的函数作为参数，并返回一个新的 CompletableFuture。
         * thenCombine 方法用于将两个 CompletableFuture 的结果组合在一起。它接受两个 CompletionStage 参数和一个 BiFunction 接口参数，该函数将在两个 CompletableFuture 的结果上被调用，并返回一个新的 CompletableFuture。
         */
        CompletableFuture<String> future = CompletableFuture.completedFuture("hello!")
                .thenApply(s -> s + "world!")
                .thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "nice!"));
        System.out.println(future.get());

        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "hello!")
                .thenCombine(CompletableFuture.supplyAsync(() -> "world!"), (s1, s2) -> s1 + s2)
                .thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "nice!"));
        System.out.println(completableFuture.get());

        CompletableFuture<Integer> futureTime = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 1;
        })
                //如果在指定时间内没完成，则返回一个指定的值
                .completeOnTimeout(0, 500, TimeUnit.MILLISECONDS);
        //如果在指定的时间内没完成，则抛出一个异常 TimeoutException
//                .orTimeout(500, TimeUnit.MILLISECONDS);
        System.out.println(futureTime.get());

        /*******************示例5**********************/
        // 创建异步执行任务
        CompletableFuture<String> cfa = CompletableFuture.supplyAsync(() -> {
            System.out.println("supplyAsync1，执行完毕");
            return "supplyAsync1";
        });
        CompletableFuture<String> cfb = CompletableFuture
                .supplyAsync(() -> {
                    System.out.println("supplyAsync2，执行完毕");
                    return "supplyAsync2";
                })
                .applyToEither(cfa, (r) -> {
                    System.out.println("第一个执行成功的任务结果：" + r);
                    return r + "_applyToEither";
                });

        System.out.println("任务执行结果：" + cfb.get());

        // 将多个任务，进行AND组合
        CompletableFuture<String> cfc = CompletableFuture
                .allOf(cfa, cfb)
                .handle((r, e) -> {
                    System.out.println("所有任务都执行成功，result:" + r);
                    return "over";
                });
        System.out.println(cfc.get());

    }
}
    class MyTask {
        private String name;
        private String msg;
        private long second = 0;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }

        public long getSecond() {
            return second;
        }

        public void setSecond(long second) {
            this.second = second;
        }

        public String taskInfo() {
            return name + ":" + msg;
        }
    }
